﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using WubiTableTransformers;

namespace MicrosoftWubiTableEditor
{
    public partial class MainForm : Form
    {
        private const string ResourceNameSpace = "MicrosoftWubiTableEditor.EmbeddedInstaller";
        private const string InstallerResourceName = "MicrosoftWubiTableInstaller.exe";

        private byte[] tableData;

        public MainForm()
        {
            InitializeComponent();
        }

        private void loadTableFile_Click(object sender, EventArgs e)
        {
            if (browseTablePathDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    byte[] bytes;
                    string fileName = browseTablePathDialog.FileName;
                    if (fileName.ToLower().EndsWith(".txt"))
                    {
                        bytes = MicrosoftWubiTable.LoadTextTableFile(fileName);
                    }
                    else
                    {
                        bytes = MicrosoftWubiTable.LoadBinaryTableFile(fileName);
                    }
                    tableData = bytes;
                    tablePath.Text = fileName;
                    installToSystem.Enabled = true;
                    saveBinaryTable.Enabled = true;
                    MessageBox.Show("加载码表成功。", "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch (FormatException ex)
                {
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                catch (IOException ex)
                {
                    MessageBox.Show("加载码表文件失败：" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void saveBinaryTable_Click(object sender, EventArgs e)
        {
            if (saveBinaryTableDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    using (Stream stream = new FileStream(saveBinaryTableDialog.FileName, FileMode.Create))
                        stream.Write(tableData, 0, tableData.Length);
                    MessageBox.Show("保存码表成功。", "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch (IOException ex)
                {
                    MessageBox.Show("保存码表文件失败：" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void installToSystem_Click(object sender, EventArgs e)
        {
            const string message = "为了安装成功，请确保微软五笔未处于使用状态，否则安装程序会因码\n" +
                                   "表被锁定而失败。建议您先将微软五笔卸载，重启Windows，然后再运\n" +
                                   "行本程序，安装完码表后再重新装上微软五笔。\n\n" +
                                   "是否尝试安装微软五笔码表？";
            if (MessageBox.Show(message, "确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                return;
            string installerPath = GetInstallerPath();
            string tempTablePath = GetTempTablePath();
            try
            {
                try
                {
                    ExtractInstaller(installerPath);
                    GenerateTempTableFile(tempTablePath);
                    ExecuteInstaller(installerPath, tempTablePath);
                }
                finally
                {
                    if (File.Exists(installerPath))
                        File.Delete(installerPath);
                    if (File.Exists(tempTablePath))
                        File.Delete(tempTablePath);
                }
                MessageBox.Show("安装码表成功。", "消息", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch
            {
                MessageBox.Show("安装码表失败。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private string GetInstallerPath()
        {
            string tempDir = Path.GetTempPath();
            string newExtension = string.Format(".{{{0}}}.exe", Guid.NewGuid());
            string baseName = Path.ChangeExtension(InstallerResourceName, newExtension);
            return Path.Combine(tempDir, baseName);
        }

        private string GetTempTablePath()
        {
            string tempDir = Path.GetTempPath();
            string baseName = string.Format("ChsWubi.{{{0}}}.lex", Guid.NewGuid());
            return Path.Combine(tempDir, baseName);
        }

        private void ExtractInstaller(string installerPath)
        {
            const string inputPath = ResourceNameSpace + "." + InstallerResourceName;
            Assembly assembly = Assembly.GetExecutingAssembly();
            using (Stream inStream = assembly.GetManifestResourceStream(inputPath))
            using (Stream outStream = new FileStream(installerPath, FileMode.Create))
            {
                byte[] buffer = new byte[4096];
                while (true)
                {
                    int count = inStream.Read(buffer, 0, buffer.Length);
                    if (count == 0)
                        break;
                    outStream.Write(buffer, 0, count);
                }
            }
        }

        private void GenerateTempTableFile(string tempTablePath)
        {
            using (Stream stream = new FileStream(tempTablePath, FileMode.Create))
                stream.Write(tableData, 0, tableData.Length);
        }

        private void ExecuteInstaller(string installerPath, string tempTablePath)
        {
            Process process = new Process();
            process.StartInfo.FileName = installerPath;
            process.StartInfo.Arguments = string.Format("\"{0}\"", tempTablePath);
            process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            process.Start();
            process.WaitForExit();
            if (process.ExitCode != 0)
                throw new InvalidOperationException();
        }
    }
}
